<?php
/**
 * Project: AIGC
 * Author: moufer <moufer@163.com>
 * Date: 2024/7/7 23:38
 */

namespace module\ai\library\channel;

use module\ai\library\channel\abstract\Llm;
use module\ai\library\channel\abstract\Platform;
use module\ai\library\channel\interface\ChatInterface;
use module\ai\library\channel\interface\ConfigInterface;
use module\ai\library\channel\interface\KeyInterface;
use module\ai\model\Channel;
use mof\exception\LogicException;
use mof\Mof;
use mof\utils\DictArray;
use think\helper\Str;

class Factory
{
    /**
     * AI平台字典
     * @return DictArray
     */
    public static function platformDict(): DictArray
    {
        static $dict = null;

        if (empty($dict)) {
            $keys = [];
            $classes = self::platformClass();
            foreach ($classes as $clsName) {
                $keys[$clsName::getFlag()] = $clsName::getName();
            }
            $dict = new DictArray($keys);
        }

        return $dict;
    }

    /**
     * 配置类
     * @param string $flag
     * @return ConfigInterface
     */
    public static function config(string $flag): ConfigInterface
    {
        if (!$classes = self::platformClass($flag)) {
            throw new LogicException('找不到该通道：' . $flag);
        }
        $class = array_pop($classes);
        return new $class();
    }

    public static function chat(Channel $channel): ChatInterface
    {
//        if (!$name = $channel->config['model']) {
//            throw new LogicException('未指定模型');
//        }
        $class = Str::studly($channel->flag . '_chat');
        if (!$models = self::chatClass($class, true)) {
            throw new LogicException('找不到指定的模型:'.$class);
        }
        $class = array_pop($models);
        return new $class($channel->oneKey()->config);
    }

    /**
     * 大语言模型类
     * @param Channel $channel
     * @return Llm
     */
    public static function model(Channel $channel): Llm
    {
        if (!$name = $channel->config['model']) {
            throw new LogicException('未指定模型');
        }

        if (str_contains($name, ':')) {
            list($class,) = explode(':', $name, 2);
        } else {
            $class = Str::studly($channel->flag);
        }

        if (!$models = self::modelClass($class, true)) {
            throw new LogicException('找不到指定的模型');
        }
        $llm = array_pop($models);
        return new $llm($channel);
    }

    /**
     * 平台密钥类
     * @param $platform
     * @return KeyInterface
     */
    public static function key($platform): KeyInterface
    {
        if (!$classes = self::platformClass($platform)) {
            throw new LogicException('找不到该平台信息:'.$platform);
        }
        $class = array_pop($classes);
        return new $class();
    }

    /**
     * 通道字典
     * @param string $platform
     * @return DictArray
     */
    public static function channelDict(string $platform = ''): DictArray
    {
        $result = [];
        $classes = self::platformClass($platform);
        foreach ($classes as $class) {
            $instance = new $class();
            $result[$instance->getFlag()] = self::platformDict()
                ->findValue($instance->getFlag());
        }
        return new DictArray($result);
    }

    /**
     * 获取配置类列表
     * @param string $platform
     * @return Platform[]|ConfigInterface[]|KeyInterface[]
     */
    public static function platformClass(string $platform = ''): array
    {
        $platform = $platform ? ($platform . '_platform') : '';
        return static::classes(
            'platform',
            $platform ? fn($class) => class_basename($class) === Str::studly($platform) : null
        );
    }

    /**
     * 获取chat类列表
     * @param string $flag
     * @param bool $isClassName
     * @return array
     */
    public static function chatClass(string $flag = '', bool $isClassName = false): array
    {
        $fn = $flag ? function ($class) use ($flag, $isClassName) {
            return $isClassName
                ? class_basename($class) === Str::studly($flag)
                : str_starts_with(class_basename($class), Str::studly($flag));
        } : null;
        return static::classes('chat', $fn);
    }

    /**
     * 获取模型类列表
     * @param string $flag
     * @param bool $isClassName
     * @return array
     */
    public static function modelClass(string $flag = '', bool $isClassName = false): array
    {
        $fn = $flag ? function ($class) use ($flag, $isClassName) {
            return $isClassName
                ? class_basename($class) === Str::studly($flag)
                : str_starts_with(class_basename($class), Str::studly($flag));
        } : null;
        return static::classes('llm', $fn);
    }

    /**
     * 获取同类型类列表
     * @param $type
     * @param $filterFunction
     * @return array
     */
    protected static function classes($type, $filterFunction = null): array
    {
        $namespace = \mof\utils\Str::namespace(Factory::class) . "\\$type";
        $dir = Mof::namespaceToDir($namespace);
        if (!is_dir($dir)) return [];

        $files = array_filter(scandir($dir), function ($file) use ($namespace, $filterFunction) {
            if (in_array($file, ['.', '..'])) return false;
            if (!$baseName = basename($file, '.php')) return false;
            $class = $namespace . '\\' . $baseName;
            if (!class_exists($class)) return false;
            return empty($filterFunction) || $filterFunction($class, $file);
        });
        if (!$files) return [];

        return array_map(fn($file) => $namespace . '\\' . basename($file, '.php'), $files);
    }

}